/*
 * A JSR-168 portlet application for previewing unread
 * email messages.
 *
 * Copyright (C) 2007  San Joaquin Delta College
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation version 2
 * of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *
 */
package edu.deltacollege.portal.portlets.emailpreview;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

import javax.mail.Address;
import javax.mail.FetchProfile;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * A Data Access Object (DAO) for retrieving email account information.
 * Currently all the information retrieved is related to the user's
 * inbox.
 *
 * @author Andreas Christoforides
 *
 */
public class AccountInfoDAO {

    public static final String POP_PROTOCOL_PREFIX = "pop3";
    public static final String IMAP_PROTOCOL_PREFIX = "imap";

    private String mailStoreProtocol;
    private String mailHost;
    private String mailPort;
    private String inboxFolderName;
    private String usernameSuffix;

    private int timeout;
    private int connectionTimeout;
    
    private String preferredEmailMessageDAOName;
    private EmailMessageDAOSelector emailMessageDAOSelector;
    private EmailMessageDAO emailMessageDAO;

    private Log log;
    

    public AccountInfoDAO () {
        this.log = LogFactory.getLog(AccountInfoDAO.class);
    }
    
    public void init() {
        emailMessageDAO = emailMessageDAOSelector.getEmailMessageDAO(this);
    }

    /**
     * Returns the mail server port used by this <code>AccountInfoDAO</code>.
     *
     * @return Returns the configured mail server port.
     */
    public String getMailPort() {
        return this.mailPort;
    }

    /**
     * Sets the mail port to be used by this <code>AccountInfoDAO</code>.
     *
     * @param mailPort The mail server port to be used.
     */
    public void setMailPort(String mailPort) {
        this.mailPort = mailPort;
    }

    /**
     * Returns the mail protocol used by this <code>AccountInfoDAO</code>.
     * The possible values are imap,imaps,pop3,pop3s.
     *
     * @return Returns the configured mail server host.
     */
    public String getMailStoreProtocol() {
        return this.mailStoreProtocol;
    }

    /**
     * Sets the mail protocol to be used by this <code>AccountInfoDAO</code>.
     * The possible values are imap,imaps,pop3,pop3s.
     *
     * @param mailStoreProtocol The mail protocol to be used.
     */
    public void setMailStoreProtocol(String mailStoreProtocol) {
        this.mailStoreProtocol = mailStoreProtocol;
    }

    /**
     * Returns the mail server host used by this <code>AccountInfoDAO</code>.
     *
     * @return Returns the configured mail server host.
     */
    public String getMailHost() {
        return mailHost;
    }

    /**
     * Sets the mail server host to be used by this <code>AccountInfoDAO</code>.
     *
     * @param mailHost The mail server host to be used.
     */
    public void setMailHost(String mailHost) {
        this.mailHost = mailHost;
    }

    /**
     * Returns the connection timeout for mail server communication.
     *
     * @return Returns the mail server connection timeout.
     */
    public int getConnectionTimeout() {
        return this.connectionTimeout;
    }

    /**
     * Sets the connection timeout for mail server communication.
     *
     * @param connectionTimeout Sets the mail server connection timeout.
     */
    public void setConnectionTimeout(int connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    /**
     * Returns the operation timeout for mail server communication.
     *
     * @return Returns the mail server operation timeout.
     */
    public int getTimeout() {
        return this.timeout;
    }

    /**
     * Sets the operation timeout for mail server communication.
     *
     * @param timeout The mail server operation timeout.
     */
    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    /**
	 * @return Returns the usernameSuffix.
	 */
	public String getUsernameSuffix() {
		return usernameSuffix;
	}

	/**
	 * @param usernameSuffix The usernameSuffix to set.
	 */
	public void setUsernameSuffix(String usernameSuffix) {
		this.usernameSuffix = usernameSuffix;
	}

	/**
     * Returns information about the email account of the user specified by the
     * provided username and password. Delegates message lookup to the injected
     * {@link EmailMessageDAO}.
     *
     * @param username The username of the user to retrieve email account information for.
     * @param password The password of the user to retrieve email account information for.
     * @return An {@link AccountInfo AccountInfo} instance that describes the email
     * account of the specified user.
     *
     * @throws EmailPreviewExcepction An {@link EmailPreviewException
     * EmailPreviewException} is thrown if an exception occurs while retrieve
     * the user's email account information.
     */
    public AccountInfo retrieveEmailAccountInfo (String username, String password)
    throws EmailPreviewException {

        Folder inbox = null;
        try {

            // Retrieve user's inbox
            inbox = getUserInbox(username, password);
            inbox.open(Folder.READ_ONLY);
            long startTime = System.currentTimeMillis();
            EmailMessageLookupResult unreadMessages = emailMessageDAO.getUnreadEmailMessages(inbox);
            int totalMessageCount = emailMessageDAO.getTotalEmailMessageCount(inbox);
            if ( log.isDebugEnabled() ) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                int unreadMessageToDisplayCount = unreadMessages.getEmailMessages().size();
                log.debug("Finished looking up email messages. Inbox size: " + totalMessageCount + 
                        " Unread message count: " + unreadMessageToDisplayCount + 
                        " Total elapsed time: " + elapsedTime + "ms " +
                        " Time per message in inbox: " + (totalMessageCount == 0 ? 0 : (elapsedTime / totalMessageCount)) + "ms" +
                        " Time per unread message: " + (unreadMessageToDisplayCount == 0 ? 0 : (elapsedTime / unreadMessageToDisplayCount)) + "ms");
            }
            inbox.close(false);
            
            // Initialize account information with information retrieved from inbox
            AccountInfo acountInfo = new AccountInfo();
            acountInfo.setUnreadMessages(unreadMessages.getEmailMessages());
            acountInfo.setUnreadMessageCount(unreadMessages.getEmailMessages().size());
            acountInfo.setTotalMessageCount(totalMessageCount);
            acountInfo.setInspectedMessageCount(unreadMessages.getInspectedMessageCount());

            if (log.isDebugEnabled()) {
                log.debug("Successfully retrieved email account info for user " + username);
            }

            return acountInfo;

        } catch (MessagingException me) {

            StringBuilder errorMsg = new StringBuilder(128);
            errorMsg.append("An error occured while retrieving ");
            errorMsg.append("email account information for user with username ");
            errorMsg.append(username);

            log.error(errorMsg.toString(), me);

            throw new EmailPreviewException(me);
        } finally {
            if ( inbox != null ) {
                try {
                    inbox.close(false);
                } catch ( Exception e ) {}
            }
        }

        
    }

    private Folder getUserInbox(String username, String password)
    throws MessagingException {

        // Initialize connection properties
        Properties mailProperties = new Properties();
        mailProperties.put("mail.store.protocol", mailStoreProtocol);
        mailProperties.put("mail.host", mailHost);
        mailProperties.put("mail.port", mailPort);

        String protocolPropertyPrefix = "mail." + mailStoreProtocol + ".";

        // Set connection timeout property
        if (connectionTimeout >= 0) {
            mailProperties.put(protocolPropertyPrefix +
                    "connectiontimeout", connectionTimeout);
        }

        // Set timeout property
        if (timeout >= 0) {
            mailProperties.put(protocolPropertyPrefix + "timeout", timeout);
        }

        // Append suffix to username
        username+= this.usernameSuffix;

        // Connect/authenticate to the configured store
        Session session = Session.getInstance(mailProperties, null);
        Store store = session.getStore();
        store.connect(username, password);

        if (log.isDebugEnabled()) {
            log.debug("Mail store created for user " + username);
        }

        // Retrieve user's inbox folder
        Folder root = store.getDefaultFolder();
        Folder inboxFolder = root.getFolder(this.inboxFolderName);

        return inboxFolder;
    }

	/**
	 * Returns the precise name of the inbox folder.
	 *
	 * @return The name of the inbox folder.
	 */
	public String getInboxFolderName() {
		return this.inboxFolderName;
	}

	/**
	 * Sets the name of the inbox folder.
	 *
	 * @param inboxFolderName The inbox folder name to set.
	 */
	public void setInboxFolderName(String inboxFolderName) {
		this.inboxFolderName = inboxFolderName;
	}
	
	public EmailMessageDAOSelector getEmailMessageDAOSelector() {
        return emailMessageDAOSelector;
    }

    public void setEmailMessageDAOSelector(EmailMessageDAOSelector emailMessageDAOSelector) {
        this.emailMessageDAOSelector = emailMessageDAOSelector;
    }

    public String getPreferredEmailMessageDAOName() {
        return preferredEmailMessageDAOName;
    }
    
    public void setPreferredEmailMessageDAOName(String daoName) {
        this.preferredEmailMessageDAOName = daoName;
    }
}
